import {exportDefaultUtils, getBaseUrl, getScopedId, IsLoaderVue, urlFileToCommon} from "./CommonUtils";
import {addCssToPage} from "./CssUtils";
import type VueType from "vue";
import {ComponentOptions} from "vue/types/options";
import {httpUtils} from "./HttpUtils";
import {parseComponent, SFCBlock} from "vue-template-compiler";
import {pathJoin} from "./PathUtils";
import {babelTransformCode} from "./BabelGenUtils";

function getMarkRange(code, startMark, endMark) {
  if (!code || !code.indexOf) {
    // TODO handle edge error
    return false
  }
  return {
    start: code.indexOf(startMark) + 1,
    end: code.lastIndexOf(endMark)
  }
}
function getFunctionBody(code) {
  const range = getMarkRange(code, '{', '}')
  return code.substring(range?.start, range?.end)
}

export function vueTemplateCompCodeUtil(code: string) {
  const render = 'var render = function render() {'
  const staticRenderFnsSpliter = '}; /* staticRenderFnsSpliter */'
  code = babelTransformCode(render + code + staticRenderFnsSpliter,'VueTemplateBabelCompiler',false,'script')
  return getFunctionBody(code)
}

export function parseFileToVueObj(url: string, vueFile: string) {
  const {template, script, styles} = parseComponent(vueFile)
  let scopedId = getScopedId(url)

  let vCom = {
    __zhiXinFileId: url,
    _scopeId: scopedId,
    props: {
      [window.__chineseTranscode__('文件路径')]: {
        type: String,
        default: url
      },
    }
  }
  if (template && template.content) { // 编译位置还是要让vue自己编译，不让容易出错
      // @ts-ignore
    vCom.template = template.content
  }
  return new Promise(resolve => {
    // 处理样式 在同步加载中，子项的样式会优先插入
    // 所以直接导出js
    const func = () => {
      addCssToPage(url, styles, scopedId)
        .then(res => {
        })
        .catch(e => {
          console.error(e)
        }).finally(() => {
        resolve(vCom)
      })
    }
    if (script) {
      exportDefaultUtils(url, script.content)
        .then(obj => {
          vCom = {
            ...vCom,
            ...obj,
            props: {
              ...obj.props,
              ...vCom.props,
            },
          }
          func()
        })
    } else {
      func()
    }
  })
}

/**
 * 整理页面转到Vue
 *    let AppVm = BrowseVueLoader.pageToVue({
 *         el: '#app', // 需要管理的控制元素
 *         name: 'App',
 *         template: '#my-component-template',
 *     },'./index.less')
 *     返回Vue的实例
 * @param options Vue的config
 * @param cssPath 生成本地样式，可有可无
 */
export function pageToVue(options: ComponentOptions<any>, cssPath?: string | string[]): VueType {
  let scopedId = 'zhixin-path-app' + new Date().getTime()
  if (cssPath) {
    if(typeof cssPath === 'string') {
      cssPath = [cssPath]
      let baseUrl = getBaseUrl('')
      cssPath = cssPath.map(item=>pathJoin(baseUrl,item))
    }
    cssPath.forEach(item => {
      httpUtils(item).then(res => {
        let sfcBlock: SFCBlock = {
          attrs: {},
          content: res,
          type: "style",
          scoped: true,
          lang: item.substring(item.lastIndexOf('.') + 1)
        }
        addCssToPage(item, [sfcBlock], scopedId).then(r => r)
      })
    })
  }
  // @ts-ignore
  return new Vue({
    ...options,
    // @ts-ignore
    _scopeId: scopedId,
  })
}

/**
 * @param url
 */
export function urlFileToVue(url: string) {
  return urlFileToCommon.call(this, url, IsLoaderVue, parseFileToVueObj)
}

/**
 * 异步加载组件
 * @param url
 */
export function urlFileToVueAsync(url: string) {
  return () => {
    return urlFileToVue.call(this, url)
  }
}


// vue错误处理

export type VueGlobalErrorHandlerFunc = (err, vm, info) => void

export const VueGlobalErrorHandler: VueGlobalErrorHandlerFunc[] = [
  (err, vm, info) => {
    console.error(window.__chineseTranscode__('错误信息：'), err)
    console.error(window.__chineseTranscode__('错误生命周期：'), info)
    console.error(window.__chineseTranscode__('错误文件路径：'), vm.$options.__zhiXinFileId)
    console.error(window.__chineseTranscode__('错误实例：'), vm)
  }
]

/**
 * 添加全局错误处理函数，提示错误在那个组件里面，以及错误的大概位置
 * @param func
 */
export function addVueGlobalErrorHandler(func?: VueGlobalErrorHandlerFunc) {
  if (func) {
    VueGlobalErrorHandler.push(func)
  }
}

if (window && window.Vue) {
  // @ts-ignore
  Vue.config.errorHandler = function (err, vm, info) {
    VueGlobalErrorHandler.forEach(i => {
      i(err, vm, info)
    })
  }
}
